;; Title=Allocate Memory
;; Category=General
;; Description=Allocates a portion of RAM for temporary use
;; Inputs=BC: Amount of RAM to allocate
;; Outputs=IX: Pointer to allocated RAM
;; Outputs=A: Error code
;; Outputs=F: nz/c=error
;; Destroys=None
AllocateMem:
	; User RAM (where programs are executed and their data is stored)
	; is organized into a dynamic set of "partitions" which contain
	; every segment of allocated memory.  Each allocation is prefaced
	; by the owning thread ID.  The next two bytes are the size of that
	; segment, and the that many bytes is the actual allocated
	; memory.  An owning thread ID of 0xFF is special and reserved for
	; free memory.  Whenever memory is allocated, a search for the first
	; segment of memory of that size or larger is performed, and that segment
	; is divided into the newly allocated memory and the remaining free
	; space.  When memory is freed, the ID is set to 0xFF and any adjacent
	; sections are merged into a larger overall freed section.
	push bc
	call SafeInterruptDI
	push af
	push hl
	push de
		inc bc
		inc bc
		inc bc
		ld hl, UserRAM
		or a
FreeMemSearchLoop:
		ld a, (hl)
		inc hl
		ld e, (hl)
		inc hl
		ld d, (hl)
		inc a
		jr z, FreeMemFound
FreeMemSearchContinue:
		add hl, de
		jr nc, FreeMemSearchLoop
		ld a, ALLOCATE_OUT_OF_MEM
		jr AllocMemEnd
FreeMemFound:
		ex de, hl
		sbc hl, bc
		add hl, bc
		ex de, hl
		jr c, FreeMemSearchContinue
		; This section of memory fits the allocation
		inc hl
		push hl
			dec hl
			ex de, hl
			sbc hl, bc
			ex de, hl
			ld a, d
			or a
			jr nz, AllocExtraSkip
			ld a, e
			cp 5
			ld a, (CurrentThreadID)
			jr nc, AllocExtraSkip
			; If the allocation size is within 4 bytes of the
			; free section size, allocate the whole section
			ex de, hl
			add hl, bc
			ex de, hl
			jr ReallocNextSkip
AllocExtraSkip:
			call AllocSection
			inc hl
			inc hl
			inc hl
			ld a, 0FFh
ReallocNextSkip:
			ld b, d
			ld c, e
			call AllocSection
AllocMemDone:
		pop ix
		ld a,ALLOCATE_SUCCESS
AllocMemEnd:
	pop de
	pop hl
	ld b,a
	pop af
	jr c,$+3
	ei
	ld a,b
	pop bc
	ret

AllocSection:
	ld (hl), b
	dec hl
	ld (hl), c
	dec hl
	ld (hl), a
	add hl, bc
	ld (hl), c
	inc hl
	ld (hl), b
	ret

;; Title=Free Memory
;; Category=General
;; Description=Frees previously allocated RAM for other programs to use
;; Inputs=IX: Pointer to allocated RAM
;; Outputs=none
;; Destroys=None
FreeMem:
	push hl
	push de
	push bc
	push af
		push ix
		pop hl
		dec hl
		ld d, (hl)
		dec hl
		ld e, (hl)
		dec hl
		push hl
			push hl
				inc hl
				inc hl
				add hl, de
				jr c, MergeNextSkip
				ld a, (hl)
				inc a
				jr nz, MergeNextSkip
				inc hl
				ld c, (hl)
				inc hl
				ld b, (hl)
				inc bc
				inc bc
				ex de, hl
				add hl, bc
				ex de, hl
MergeNextSkip:
			pop hl
			ld bc, -UserRAM-1&UserRAMEnd
			add hl, bc
			jr nc, MergePreviousSkip
			sbc hl, bc
			ld b, (hl)
			dec hl
			ld c, (hl)
			or a
			sbc hl, bc
			ld a, (hl)
			inc a
			jr nz, MergePreviousSkip
			inc bc
			inc bc
			ex de, hl
			add hl, bc
			ex de, hl
		pop bc
		push hl
MergePreviousSkip:
		pop hl
		ld (hl), 0FFh
		inc hl
		ld (hl), e
		inc hl
		ld (hl), d
	pop af
	pop bc
	pop de
	pop hl
	ret
; Clears all memory accociated with a thread
; inputs	a  = Thread ID
FreeThreadMem:
	push af
	push de
	push hl
	push ix
	ld hl,UserRAM
_		cp (hl)
			jr nz,_
		push hl
		pop ix
		call FreeMem
_		inc hl
		ld e,(hl)
		inc hl
		ld d,(hl)
		add hl,de		; jump to next section
		ld de,UserRAMEnd	; get the end of UserRAM
		sbc hl,de		; compare
		add hl,de
		jr c,--_		; loop again if hl < UserRAMEnd
	pop ix
	pop hl
	pop de
	pop af
	ret
; Returns the amount of Free RAM
; output	hl = Free RAM
ReturnFreeRAM:
	push af
	push de
	xor a
	ld d,a
	ld e,a
	dec a
	ld hl,UserRAM
_		cp (hl)
		inc hl
		ld c,(hl)
		inc hl
		ld b,(hl)
			jr nz,_
		ex de,hl
		add hl,bc
		ex de,hl
_		add hl,bc		; jump to next section
		ld bc,UserRAMEnd	; get the end of UserRAM
		sbc hl,bc		; compare
		add hl,bc
		jr c,--_		; loop again if hl < UserRAMEnd
	ex de,hl
	pop de
	pop af
	ret
